<!DOCTYPE html>
<html>
<head>
<title>django教程day06.md</title>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">

<style>
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

body {
	font-family: "Segoe WPC", "Segoe UI", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback";
	font-size: 14px;
	padding: 0 12px;
	line-height: 22px;
	word-wrap: break-word;
}

#code-csp-warning {
	position: fixed;
	top: 0;
	right: 0;
	color: white;
	margin: 16px;
	text-align: center;
	font-size: 12px;
	font-family: sans-serif;
	background-color:#444444;
	cursor: pointer;
	padding: 6px;
	box-shadow: 1px 1px 1px rgba(0,0,0,.25);
}

#code-csp-warning:hover {
	text-decoration: none;
	background-color:#007acc;
	box-shadow: 2px 2px 2px rgba(0,0,0,.25);
}


body.scrollBeyondLastLine {
	margin-bottom: calc(100vh - 22px);
}

body.showEditorSelection .code-line {
	position: relative;
}

body.showEditorSelection .code-active-line:before,
body.showEditorSelection .code-line:hover:before {
	content: "";
	display: block;
	position: absolute;
	top: 0;
	left: -12px;
	height: 100%;
}

body.showEditorSelection li.code-active-line:before,
body.showEditorSelection li.code-line:hover:before {
	left: -30px;
}

.vscode-light.showEditorSelection .code-active-line:before {
	border-left: 3px solid rgba(0, 0, 0, 0.15);
}

.vscode-light.showEditorSelection .code-line:hover:before {
	border-left: 3px solid rgba(0, 0, 0, 0.40);
}

.vscode-dark.showEditorSelection .code-active-line:before {
	border-left: 3px solid rgba(255, 255, 255, 0.4);
}

.vscode-dark.showEditorSelection .code-line:hover:before {
	border-left: 3px solid rgba(255, 255, 255, 0.60);
}

.vscode-high-contrast.showEditorSelection .code-active-line:before {
	border-left: 3px solid rgba(255, 160, 0, 0.7);
}

.vscode-high-contrast.showEditorSelection .code-line:hover:before {
	border-left: 3px solid rgba(255, 160, 0, 1);
}

img {
	max-width: 100%;
	max-height: 100%;
}

a {
	color: #4080D0;
	text-decoration: none;
}

a:focus,
input:focus,
select:focus,
textarea:focus {
	outline: 1px solid -webkit-focus-ring-color;
	outline-offset: -1px;
}

hr {
	border: 0;
	height: 2px;
	border-bottom: 2px solid;
}

h1 {
	padding-bottom: 0.3em;
	line-height: 1.2;
	border-bottom-width: 1px;
	border-bottom-style: solid;
}

h1, h2, h3 {
	font-weight: normal;
}

h1 code,
h2 code,
h3 code,
h4 code,
h5 code,
h6 code {
	font-size: inherit;
	line-height: auto;
}

a:hover {
	color: #4080D0;
	text-decoration: underline;
}

table {
	border-collapse: collapse;
}

table > thead > tr > th {
	text-align: left;
	border-bottom: 1px solid;
}

table > thead > tr > th,
table > thead > tr > td,
table > tbody > tr > th,
table > tbody > tr > td {
	padding: 5px 10px;
}

table > tbody > tr + tr > td {
	border-top: 1px solid;
}

blockquote {
	margin: 0 7px 0 5px;
	padding: 0 16px 0 10px;
	border-left: 5px solid;
}

code {
	font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback";
	font-size: 14px;
	line-height: 19px;
}

body.wordWrap pre {
	white-space: pre-wrap;
}

.mac code {
	font-size: 12px;
	line-height: 18px;
}

pre:not(.hljs),
pre.hljs code > div {
	padding: 16px;
	border-radius: 3px;
	overflow: auto;
}

/** Theming */

.vscode-light,
.vscode-light pre code {
	color: rgb(30, 30, 30);
}

.vscode-dark,
.vscode-dark pre code {
	color: #DDD;
}

.vscode-high-contrast,
.vscode-high-contrast pre code {
	color: white;
}

.vscode-light code {
	color: #A31515;
}

.vscode-dark code {
	color: #D7BA7D;
}

.vscode-light pre:not(.hljs),
.vscode-light code > div {
	background-color: rgba(220, 220, 220, 0.4);
}

.vscode-dark pre:not(.hljs),
.vscode-dark code > div {
	background-color: rgba(10, 10, 10, 0.4);
}

.vscode-high-contrast pre:not(.hljs),
.vscode-high-contrast code > div {
	background-color: rgb(0, 0, 0);
}

.vscode-high-contrast h1 {
	border-color: rgb(0, 0, 0);
}

.vscode-light table > thead > tr > th {
	border-color: rgba(0, 0, 0, 0.69);
}

.vscode-dark table > thead > tr > th {
	border-color: rgba(255, 255, 255, 0.69);
}

.vscode-light h1,
.vscode-light hr,
.vscode-light table > tbody > tr + tr > td {
	border-color: rgba(0, 0, 0, 0.18);
}

.vscode-dark h1,
.vscode-dark hr,
.vscode-dark table > tbody > tr + tr > td {
	border-color: rgba(255, 255, 255, 0.18);
}

.vscode-light blockquote,
.vscode-dark blockquote {
	background: rgba(127, 127, 127, 0.1);
	border-color: rgba(0, 122, 204, 0.5);
}

.vscode-high-contrast blockquote {
	background: transparent;
	border-color: #fff;
}
</style>

<style>
/* Tomorrow Theme */
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
/* Original theme - https://github.com/chriskempson/tomorrow-theme */

/* Tomorrow Comment */
.hljs-comment,
.hljs-quote {
	color: #8e908c;
}

/* Tomorrow Red */
.hljs-variable,
.hljs-template-variable,
.hljs-tag,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class,
.hljs-regexp,
.hljs-deletion {
	color: #c82829;
}

/* Tomorrow Orange */
.hljs-number,
.hljs-built_in,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params,
.hljs-meta,
.hljs-link {
	color: #f5871f;
}

/* Tomorrow Yellow */
.hljs-attribute {
	color: #eab700;
}

/* Tomorrow Green */
.hljs-string,
.hljs-symbol,
.hljs-bullet,
.hljs-addition {
	color: #718c00;
}

/* Tomorrow Blue */
.hljs-title,
.hljs-section {
	color: #4271ae;
}

/* Tomorrow Purple */
.hljs-keyword,
.hljs-selector-tag {
	color: #8959a8;
}

.hljs {
	display: block;
	overflow-x: auto;
	color: #4d4d4c;
	padding: 0.5em;
}

.hljs-emphasis {
	font-style: italic;
}

.hljs-strong {
	font-weight: bold;
}
</style>

<style>
/*
 * Markdown PDF CSS
 */

 body {
	font-family:  "Meiryo", "Segoe WPC", "Segoe UI", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback";
}

pre {
	background-color: #f8f8f8;
	border: 1px solid #cccccc;
	border-radius: 3px;
	overflow-x: auto;
	white-space: pre-wrap;
	overflow-wrap: break-word;
}

pre:not(.hljs) {
	padding: 23px;
	line-height: 19px;
}

blockquote {
	background: rgba(127, 127, 127, 0.1);
	border-color: rgba(0, 122, 204, 0.5);
}

.emoji {
	height: 1.4em;
}

/* for inline code */
:not(pre):not(.hljs) > code {
	color: #C9AE75; /* Change the old color so it seems less like an error */
	font-size: inherit;
}

/* Page Break : use <div class="page"/> to insert page break
-------------------------------------------------------- */
.page {
	page-break-after: always;
}

</style>

</head>
<body>
<h1 id="%E3%80%8Adjango-%E6%95%99%E7%A8%8B%E3%80%8B">《Django 教程》</h1>
<ul>
<li>讲师: 魏明择</li>
<li>时间: 2019</li>
</ul>
<h2 id="%E7%9B%AE%E5%BD%95">目录</h2>
<!-- TOC depthFrom:3 depthTo:5 -->
<!-- /TOC -->
<h3 id="%E6%95%B0%E6%8D%AE%E8%A1%A8%E5%85%B3%E8%81%94%E5%85%B3%E7%B3%BB%E6%98%A0%E5%B0%84-relationship-map">数据表关联关系映射 Relationship Map</h3>
<ul>
<li>在关系型数据库中，通常不会把所有数据都放在同一张表中，这样做会额外占用内存空间，</li>
<li>在关系列数据库中通常用表关联来解决数据库。</li>
<li>常用的表关联方式有三种:
<ol>
<li>一对一映射
<ul>
<li>如: 一个身份证对应一个人</li>
</ul>
</li>
<li>一对多映射
<ul>
<li>如: 一个班级可以有多个学生</li>
</ul>
</li>
<li>多对多映射
<ul>
<li>如: 一个学生可以报多个课程，一个课程可以有多个学生学习</li>
</ul>
</li>
</ol>
</li>
</ul>
<h4 id="%E4%B8%80%E5%AF%B9%E4%B8%80%E6%98%A0%E5%B0%84">一对一映射</h4>
<ul>
<li>一对一是表示现实事物间存在的一对一的对应关系。</li>
<li>如:一个家庭只有一个户主，一个男人有一个妻子，一个人有一个唯一的指纹信息等</li>
</ul>
<ol>
<li>语法<pre class="hljs"><code><div>在关联的两个类中的任何一个类中:
class A(model.Model):
    ...

class B(model.Model):
    属性 = models.OneToOneField(A)
</div></code></pre>
</li>
<li>用法示例
<ol>
<li>创建作家和作家妻子类<pre class="hljs"><code><div><span class="hljs-comment"># file : xxxxxxxx/models.py</span>
<span class="hljs-keyword">from</span> django.db <span class="hljs-keyword">import</span> models

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Author</span><span class="hljs-params">(models.Model)</span>:</span>
    <span class="hljs-string">'''作家模型类'''</span>
    name = models.CharField(<span class="hljs-string">'作家'</span>, max_length=<span class="hljs-number">50</span>)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Wife</span><span class="hljs-params">(models.Model)</span>:</span>
    <span class="hljs-string">'''作家妻子模型类'''</span>
    name = models.CharField(<span class="hljs-string">"妻子"</span>, max_length=<span class="hljs-number">50</span>)
    author = models.OneToOneField(Author)  <span class="hljs-comment"># 增加一对一属性</span>
</div></code></pre>
</li>
<li>查询
<ul>
<li>在 Wife 对象中,通过 author 属性找到对应的author对象</li>
<li>在 Author 对象中,通过 wife 属性找到对应的wife对象</li>
</ul>
</li>
<li>创始一对一的数据记录<pre class="hljs"><code><div><span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span> models
author1 = models.Author.objects.create(name=<span class="hljs-string">'王老师'</span>)
wife1 = models.Wife.objects.create(name=<span class="hljs-string">'王夫人'</span>, author=author1)  <span class="hljs-comment"># 关联王老师</span>
author2 = models.Author.objects.create(name=<span class="hljs-string">'小泽老师'</span>)  <span class="hljs-comment"># 一对一可以没有数据对应的数据 </span>
</div></code></pre>
</li>
<li>一对一数据的相互获取
<ol>
<li>正向查询
<ul>
<li>直接通过关联属性查询即可</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-comment"># 通过 wife 找 author</span>
<span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span> models
wife = models.Wife.objects.get(name=<span class="hljs-string">'王夫人'</span>)
print(wife.name, <span class="hljs-string">'的老公是'</span>, wife.author.name)
</div></code></pre>
</li>
<li>反向查询
<ul>
<li>通过反向引用属性查询</li>
<li>反向引用属性为<code>实例对象.引用类名(小写)</code>，如作家的反向引用为<code>作家对象.wife</code></li>
<li>当反向引用不存在时，则会触发异常</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-comment"># 通过 author.wife 引用属性 找 wife,如果没有对应的wife刚触发异常</span>
author1 = models.Author.objects.get(name=<span class="hljs-string">'王老师'</span>)
print(author1.name, <span class="hljs-string">'的妻子是'</span>, author1.wife.name)
author2 = models.Author.objects.get(name=<span class="hljs-string">'小泽老师'</span>)
<span class="hljs-keyword">try</span>:
    print(author2.name, <span class="hljs-string">'的妻子是'</span>, author2.wife.name)
<span class="hljs-keyword">except</span>:
    print(author2.name, <span class="hljs-string">'还没有妻子'</span>)
</div></code></pre>
</li>
</ol>
</li>
</ol>
</li>
</ol>
<ul>
<li>
<p>作用:</p>
<ul>
<li>主要是解决常用数据不常用数据的存储问题,把经常加载的一个数据放在主表中，不常用数据放在另一个副表中，这样在访问主表数据时不需要加载副表中的数据以提高访问速度提高效率和节省内存空间,如经常把书的内容和书名建成两张表，因为在网站上经常访问书名等信息，但不需要得到书的内容。</li>
</ul>
</li>
<li>
<p>练习:</p>
<ol>
<li>创建一个Wife模型类,属性如下
<ol>
<li>name</li>
<li>age</li>
</ol>
</li>
<li>在Wife类中增加一对一关联关系,引用 Author</li>
<li>同步回数据库并观察结果</li>
</ol>
</li>
</ul>
<h4 id="%E4%B8%80%E5%AF%B9%E5%A4%9A%E6%98%A0%E5%B0%84">一对多映射</h4>
<ul>
<li>一对多是表示现实事物间存在的一对多的对应关系。</li>
<li>如:一个学校有多个班级,一个班级有多个学生, 一本图书只能属于一个出版社,一个出版社允许出版多本图书</li>
</ul>
<ol>
<li>
<p>用法语法</p>
<ul>
<li>当一个A类对象可以关联多个B类对象时</li>
</ul>
<pre class="hljs"><code><div>class A(model.Model):
    ...

class B(model.Model):
    属性 = models.ForeignKey(多对一中&quot;一&quot;的模型类, ...)
</div></code></pre>
</li>
<li>
<p>外键类ForeignKey</p>
<ul>
<li>构造函数:<pre class="hljs"><code><div>ForeignKey(to, on_delete, **options)
</div></code></pre>
</li>
<li>常用参数:
<ul>
<li>on_delete
<ol>
<li>models.CASCADE  级联删除。 Django模拟SQL约束ON DELETE CASCADE的行为，并删除包含ForeignKey的对象。</li>
<li>models.PROTECT 抛出ProtectedError 以阻止被引用对象的删除;</li>
<li>SET_NULL 设置ForeignKey null；只有null是True才有可能。</li>
<li>SET_DEFAULT  将ForeignKey设置为其默认值；必须设置ForeignKey的默认值。</li>
<li>... 其它参请参考文档 <a href="https://yiyibooks.cn/xx/Django_1.11.6/ref/index.html">https://yiyibooks.cn/xx/Django_1.11.6/ref/index.html</a> ForeignKey部分</li>
</ol>
</li>
<li><code>**options</code> 可以是常用的字段选项如:
<ol>
<li>null</li>
<li>unique等</li>
<li>...</li>
</ol>
</li>
</ul>
</li>
</ul>
</li>
<li>
<p>示例</p>
<ul>
<li>有二个出版社对应五本书的情况.
<ol>
<li>
<p><code>清华大学出版社</code> 有书</p>
<ol>
<li>C++</li>
<li>Java</li>
<li>Python</li>
</ol>
</li>
<li>
<p><code>北京大学出版社</code> 有书</p>
<ol>
<li>西游记</li>
<li>水浒</li>
</ol>
</li>
</ol>
</li>
</ul>
<ol>
<li>定义一对多类<pre class="hljs"><code><div><span class="hljs-comment"># file: myorm/models.py</span>
<span class="hljs-keyword">from</span> django.db <span class="hljs-keyword">import</span> models
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Publisher</span><span class="hljs-params">(models.Model)</span>:</span>
    <span class="hljs-string">'''出版社'''</span>
    name = models.CharField(<span class="hljs-string">'名称'</span>, max_length=<span class="hljs-number">50</span>, unique=<span class="hljs-keyword">True</span>)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Book</span><span class="hljs-params">(models.Model)</span>:</span>
    title = models.CharField(<span class="hljs-string">'书名'</span>, max_length=<span class="hljs-number">50</span>)
    publisher = models.ForeignKey(Publisher, null=<span class="hljs-keyword">True</span>)

</div></code></pre>
</li>
</ol>
<ul>
<li>
<p>创建一对多的对象</p>
<pre class="hljs"><code><div><span class="hljs-comment"># file: xxxxx/views.py</span>
<span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span> models
pub1 = models.Publisher.objects.create(name=<span class="hljs-string">'清华大学出版社'</span>)
models.Book.objects.create(title=<span class="hljs-string">'C++'</span>, publisher=pub1)
models.Book.objects.create(title=<span class="hljs-string">'Java'</span>, publisher=pub1)
models.Book.objects.create(title=<span class="hljs-string">'Python'</span>, publisher=pub1)

pub2 = models.Publisher.objects.create(name=<span class="hljs-string">'北京大学出版社'</span>)
models.Book.objects.create(title=<span class="hljs-string">'西游记'</span>, publisher=pub2)
models.Book.objects.create(title=<span class="hljs-string">'水浒'</span>, publisher=pub2)
</div></code></pre>
</li>
<li>
<p>查询:</p>
<ul>
<li>通过多查一</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-comment"># 通过一本书找到对应的出版社</span>
abook = models.Book.objects.get(id=<span class="hljs-number">1</span>)
print(abook.title, <span class="hljs-string">'的出版社是:'</span>, abook.publisher.name)
</div></code></pre>
<ul>
<li>通过一查多</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-comment"># 通过出版社查询对应的书</span>
pub1 = models.Publisher.objects.get(name=<span class="hljs-string">'清华大学出版社'</span>)
books = pub1.book_set.all()  <span class="hljs-comment"># 通过book_set 获取pub1对应的多个Book数据对象</span>
<span class="hljs-comment"># books = models.Book.objects.filter(publisher=pub1)  # 也可以采用此方式获取</span>
print(<span class="hljs-string">"清华大学出版社的书有:"</span>)
<span class="hljs-keyword">for</span> book <span class="hljs-keyword">in</span> books:
    print(book.title)
</div></code></pre>
</li>
</ul>
</li>
</ol>
<ul>
<li>练习:
<ol>
<li>完成Book 和 Publisher 之间的一对多</li>
<li>查看数据库效果</li>
<li>登录到后台,查看Book实体</li>
</ol>
</li>
</ul>
<ol start="3">
<li>数据查询
<ol>
<li>通过 Book 查询 Publisher<pre class="hljs"><code><div>通过 publisher 属性查询即可
练习:
    查询 西游记 对应的出版社信息,打印在终端上
</div></code></pre>
</li>
<li>通过 Publisher 查询 对应的所有的 Books<pre class="hljs"><code><div>Django会在Publisher中增加一个属性来表示对对应的Book们的查询引用
属性:book_set(Entry.objects)
</div></code></pre>
</li>
</ol>
</li>
</ol>
<h4 id="%E5%A4%9A%E5%AF%B9%E5%A4%9A%E6%98%A0%E5%B0%84">多对多映射</h4>
<ul>
<li>多对多表达对象之间多对多复杂关系，如: 每个人都有不同的学校(小学，初中，高中,...),每个学校都有不同的学生...</li>
</ul>
<ol>
<li>
<p>语法</p>
<pre class="hljs"><code><div>在关联的两个类中的任意一个类中,增加:
属性 = models.ManyToManyField(Entry)
</div></code></pre>
</li>
<li>
<p>示例</p>
<pre class="hljs"><code><div>一个作者可以出版多本图书
一本图书可以被多名作者同时编写

class Author(models.Model):
    xxxx xxxx

class Book(models.Model):
    xxxx xxxx

    authors = models.ManyToManyField(Author)
</div></code></pre>
</li>
<li>
<p>数据查询</p>
<ol>
<li>通过 Book 查询对应的所有的 Authors<pre class="hljs"><code><div>可以通过authors表示对应所有Author的查询对象

book.authors.all() -&gt; 获取 book 对应的所有的author的信息

book.authors.filter(age__gt=80) -&gt; 获取book对应的作者中年龄大于80岁的作者的信息
</div></code></pre>
</li>
<li>通过 Author 查询对应的所有的Books<pre class="hljs"><code><div>Django会生成一个属性 book_set 用于表示对对应的book的查询对象相关操作
author.book_set.all()
author.book_set.filter()
author.book_set.create(...)  # 创建新书并联作用author
author.book_set.add(book)   # 添加已有的书为当前作者author
author.book_set.clear()  # 删除author所有并联的书
author.book_set.remove()  # 删除所author所有并联的书
</div></code></pre>
</li>
</ol>
</li>
<li>
<p>示例:</p>
<ul>
<li>多对多模型</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Author</span><span class="hljs-params">(models.Model)</span>:</span>
    <span class="hljs-string">'''作家模型类'''</span>
    name = models.CharField(<span class="hljs-string">'作家'</span>, max_length=<span class="hljs-number">50</span>)
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__str__</span><span class="hljs-params">(self)</span>:</span>
        <span class="hljs-keyword">return</span> self.name
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Book</span><span class="hljs-params">(models.Model)</span>:</span>
    title = models.CharField(<span class="hljs-string">'书名'</span>, max_length=<span class="hljs-number">50</span>)
    author = models.ManyToManyField(Author, null=<span class="hljs-keyword">True</span>)
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__str__</span><span class="hljs-params">(self)</span>:</span>
        <span class="hljs-keyword">return</span> self.title
</div></code></pre>
<ul>
<li>多对多视图操作</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-keyword">from</span> django.http <span class="hljs-keyword">import</span> HttpResponse

<span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span> models

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">many2many_init</span><span class="hljs-params">(request)</span>:</span>
    <span class="hljs-comment"># 创建两人个作者</span>
    author1 = models.Author.objects.create(name=<span class="hljs-string">'吕泽'</span>)
    author2 = models.Author.objects.create(name=<span class="hljs-string">'魏老师'</span>)

    <span class="hljs-comment"># 吕择和魏老师同时写了一本Python</span>
    book11 = author1.book_set.create(title=<span class="hljs-string">"Python"</span>)
    author2.book_set.add(book11)  <span class="hljs-comment">#</span>

    <span class="hljs-comment"># 魏老师还写了两本书</span>
    book21 = author2.book_set.create(title=<span class="hljs-string">"C"</span>)  <span class="hljs-comment"># 创建一本新书"C"</span>
    book22 = author2.book_set.create(title=<span class="hljs-string">"C++"</span>)  <span class="hljs-comment"># 创建一本新书"C++"</span>

    <span class="hljs-keyword">return</span> HttpResponse(<span class="hljs-string">"初始化成功"</span>)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">show_many2many</span><span class="hljs-params">(request)</span>:</span>
    authors = models.Author.objects.all()
    <span class="hljs-keyword">for</span> auth <span class="hljs-keyword">in</span> authors:
        print(<span class="hljs-string">"作者:"</span>, auth.name, <span class="hljs-string">'发出版了'</span>, auth.book_set.count(), <span class="hljs-string">'本书: '</span>)
        <span class="hljs-keyword">for</span> book <span class="hljs-keyword">in</span> books:
            print(<span class="hljs-string">'    '</span>, book.title)
    print(<span class="hljs-string">"----显示书和作者的关系----"</span>)
    books = models.Book.objects.all()
    <span class="hljs-keyword">for</span> book <span class="hljs-keyword">in</span> books:
        auths = book.author.all()
        print(book.title, <span class="hljs-string">'的作者是:'</span>, <span class="hljs-string">'、'</span>.join([str(x.name) <span class="hljs-keyword">for</span> x <span class="hljs-keyword">in</span> auths]))
    <span class="hljs-keyword">return</span> HttpResponse(<span class="hljs-string">"显示成功，请查看服务器端控制台终端"</span>)
</div></code></pre>
<ul>
<li>多对多最终的SQL结果</li>
</ul>
<pre class="hljs"><code><div>mysql&gt; <span class="hljs-keyword">select</span> * <span class="hljs-keyword">from</span> myorm2_author;
+<span class="hljs-comment">----+-----------+</span>
| id | name      |
+<span class="hljs-comment">----+-----------+</span>
| 11 | 吕泽      |
| 12 | 魏老师    |
+<span class="hljs-comment">----+-----------+</span>
2 rows in <span class="hljs-keyword">set</span> (<span class="hljs-number">0.00</span> sec)

mysql&gt; <span class="hljs-keyword">select</span> * <span class="hljs-keyword">from</span> myorm2_book;
+<span class="hljs-comment">----+--------+</span>
| id | title  |
+<span class="hljs-comment">----+--------+</span>
| 13 | Python |
| 14 | C      |
| 15 | C++    |
+<span class="hljs-comment">----+--------+</span>
3 rows in <span class="hljs-keyword">set</span> (<span class="hljs-number">0.00</span> sec)

mysql&gt; <span class="hljs-keyword">select</span> * <span class="hljs-keyword">from</span> myorm2_book_author;
+<span class="hljs-comment">----+---------+-----------+</span>
| id | book_id | author_id |
+<span class="hljs-comment">----+---------+-----------+</span>
| 17 |      13 |        11 |
| 20 |      13 |        12 |
| 18 |      14 |        12 |
| 19 |      15 |        12 |
+<span class="hljs-comment">----+---------+-----------+</span>
4 rows in <span class="hljs-keyword">set</span> (<span class="hljs-number">0.00</span> sec)
</div></code></pre>
<ul>
<li>示例示意图
<img src="images/manytomany.png" alt=""></li>
</ul>
</li>
</ol>
<h3 id="cookies-%E5%92%8C-session%E4%BC%9A%E8%AF%9D">cookies 和 session(会话)</h3>
<h4 id="cookies">cookies</h4>
<ul>
<li>cookies是保存在客户端浏览器上的存储空间，通常用来记录浏览器端自己的信息和当前连接的确认信息</li>
<li>cookies 在浏览器上是以键-值对的形式进行存储的，键和值都是以ASCII字符串的形存储(不能是中文字符串)</li>
<li>在Django 服务器端来设置 设置浏览器的COOKIE 必须通过 HttpResponse 对象来完成</li>
<li>HttpResponse 关于COOKIE的方法
<ul>
<li>添加、修改COOKIE
<ul>
<li>HttpResponse.set_cookie(key, value='', max_age=None, expires=None)
<ul>
<li>key:cookie的名字</li>
<li>value:cookie的值</li>
<li>max_age:cookie存活时间，秒为单位</li>
<li>expires:具体过期时间</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<!-- 
            - path：cookie的访问路径，只有在某个路径下访问
            - domain:域名，只有在某个域名下访问
 -->
<pre><code>- 删除COOKIE
    - HttpResponse.delete_cookie(key)
    - 删除指定的key 的Cookie。 如果key 不存在则什么也不发生。
</code></pre>
<ul>
<li>Django中的cookies
<ul>
<li>使用 响应对象HttpResponse 等 将cookie保存进客户端
<ol>
<li>方法1<pre class="hljs"><code><div><span class="hljs-keyword">from</span> django.http <span class="hljs-keyword">import</span> HttpResponse
resp = HttpResponse()
resp.set_cookie(<span class="hljs-string">'cookies名'</span>, cookies值, 超期时间)
</div></code></pre>
<ul>
<li>如:</li>
</ul>
<pre class="hljs"><code><div>resp = HttpResponse()
resp.set_cookie(<span class="hljs-string">'myvar'</span>, <span class="hljs-string">"weimz"</span>, 超期时间)
</div></code></pre>
</li>
<li>方法二, 使用render对象<pre class="hljs"><code><div><span class="hljs-keyword">from</span> django.shortcuts <span class="hljs-keyword">import</span> render
resp = render(request,<span class="hljs-string">'xxx.html'</span>,locals())
resp.set_cookie(<span class="hljs-string">'cookies名'</span>, cookies值, 超期时间)
</div></code></pre>
</li>
<li>方法三, 使用redirect对象<pre class="hljs"><code><div><span class="hljs-keyword">from</span> django.shortcuts <span class="hljs-keyword">import</span> redirect
resp = redirect(<span class="hljs-string">'/'</span>)
resp.set_cookie(<span class="hljs-string">'cookies名'</span>, cookies值, 超期时间)
</div></code></pre>
</li>
</ol>
</li>
</ul>
<ol start="3">
<li>获取cookie
<ul>
<li>通过 request.COOKIES 绑定的字典(dict) 获取客户端的 COOKIES数据<pre class="hljs"><code><div>value = request.COOKIES.get(<span class="hljs-string">'cookies名'</span>, <span class="hljs-string">'没有值!'</span>)
print(<span class="hljs-string">"cookies名 = "</span>, value)
</div></code></pre>
</li>
</ul>
</li>
<li>注:
<ul>
<li>Chrome 浏览器 可能通过开发者工具的 <code>Application</code> &gt;&gt; <code>Storage</code> &gt;&gt; <code>Cookies</code> 查看和操作浏览器端所有的 Cookies 值</li>
</ul>
</li>
</ol>
</li>
<li>cookies 示例</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-comment"># file : &lt;项目名&gt;/urls.py</span>
<span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span> views

urlpatterns = [
    url(<span class="hljs-string">r'^admin/'</span>, admin.site.urls),
    <span class="hljs-comment"># 增删改cookie</span>
    url(<span class="hljs-string">r'^add_cookie'</span>, views.add_cookie),
    url(<span class="hljs-string">r'^mod_cookie/(\d+)'</span>, views.mod_cookie),
    url(<span class="hljs-string">r'^del_cookie'</span>, views.del_cookie),
    url(<span class="hljs-string">r'^show_cookie'</span>, views.show_cookie),
]
    
<span class="hljs-comment"># file : &lt;项目名&gt;/views.py</span>
<span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span> views
<span class="hljs-keyword">from</span> django.http <span class="hljs-keyword">import</span> HttpResponse
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">add_cookie</span><span class="hljs-params">(request)</span>:</span>
    responds = HttpResponse(<span class="hljs-string">"已添加mycookie_var1,值为123"</span>)
    responds.set_cookie(<span class="hljs-string">'mycookie_var1'</span>, <span class="hljs-number">123</span>, <span class="hljs-number">3600</span>)
    <span class="hljs-keyword">return</span> responds

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">mod_cookie</span><span class="hljs-params">(request, new_value)</span>:</span>
    responds = HttpResponse(<span class="hljs-string">"已修改mycookie_var1,新值为"</span>+new_value)
    responds.set_cookie(<span class="hljs-string">'mycookie_var1'</span>, new_value, <span class="hljs-number">3600</span>)
    <span class="hljs-keyword">return</span> responds

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">del_cookie</span><span class="hljs-params">(request)</span>:</span>
    responds = HttpResponse(<span class="hljs-string">"已删除mycookie_var1"</span>)
    responds.delete_cookie(<span class="hljs-string">'mycookie_var1'</span>)
    <span class="hljs-keyword">return</span> responds

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">show_cookie</span><span class="hljs-params">(request)</span>:</span>
    value = request.COOKIES.get(<span class="hljs-string">'mycookie_var1'</span>, <span class="hljs-string">'没有值!'</span>)
    print(<span class="hljs-string">"cookie mycookie_var1 = "</span>, value)
    <span class="hljs-keyword">return</span> HttpResponse(<span class="hljs-string">"mycookie_var1:"</span> + value)
</div></code></pre>
<h4 id="session-%E4%BC%9A%E8%AF%9D">session 会话</h4>
<ul>
<li>
<p>session是在服务器上开辟一段空间用于保留浏览器和服务器交互时的重要数据</p>
</li>
<li>
<p>每个客户端都可以在服务器端有一个独立的Session</p>
</li>
<li>
<p>http协议是无状态的：每次请求都是一次新的请求，不会记得之前通信的状态</p>
</li>
<li>
<p>客户端与服务器端的一次通信，就是一次会话</p>
</li>
<li>
<p>实现状态保持的方式：在客户端或服务器端存储与会话有关的数据</p>
</li>
<li>
<p>推荐使用sesison方式，所有数据存储在服务器端，在客户端cookie中存储session_id</p>
</li>
<li>
<p>注意：不同的请求者之间不会共享这个数据，与请求者一一对应</p>
</li>
<li>
<p>什么是session</p>
<ul>
<li>session - 会话</li>
<li>在服务器上开辟一段空间用于保留浏览器和服务器交互时的重要数据</li>
</ul>
</li>
<li>
<p>Django启用Session</p>
<ul>
<li>在 settings.py 文件中</li>
<li>项INSTALLED_APPS列表中添加：<pre class="hljs"><code><div>INSTALLED_APPS = [
    <span class="hljs-comment"># 启用 sessions 应用</span>
    <span class="hljs-string">'django.contrib.sessions'</span>,
]
</div></code></pre>
</li>
<li>项MIDDLEWARE_CLASSES列表中添加：<pre class="hljs"><code><div>MIDDLEWARE = [
    <span class="hljs-comment"># 启用 Session 中间层</span>
    <span class="hljs-string">'django.contrib.sessions.middleware.SessionMiddleware'</span>,
]
</div></code></pre>
</li>
</ul>
</li>
<li>
<p>session的基本操作:</p>
<ul>
<li>
<p>session对于象是一个在似于字典的SessionStore类型的对象, 可以用类拟于字典的方式进行操作</p>
</li>
<li>
<p>session 只能够存储能够序列化的数据,如字典，列表等。</p>
</li>
<li>
<p>保存 session 的值到服务器</p>
<ul>
<li><code>request.session[键] = 值</code></li>
<li>如: <code>request.session['KEY'] = VALUE</code></li>
</ul>
</li>
<li>
<p>获取session的值</p>
<ul>
<li><code>VALUE = request.session['KEY']</code></li>
<li>或</li>
<li><code>VALUE = request.session.get('KEY', 缺省值)</code></li>
</ul>
</li>
<li>
<p>删除session的值</p>
<ul>
<li><code>del request.session['KEY']</code></li>
</ul>
</li>
<li>
<p>在 settings.py 中有关 session 的设置</p>
<ol>
<li>SESSION_COOKIE_AGE
作用:指定sessionid在cookies中的保存时长
SESSION_COOKIE_AGE = 60*30</li>
<li>SESSION_EXPIRE_AT_BROWSER_CLOSE = True
设置只要浏览器关闭时,session就失效</li>
</ol>
</li>
</ul>
</li>
<li>
<p>注: 当使用session时需要迁移数据库,否则会出现错误</p>
</li>
</ul>
<pre class="hljs"><code><div>$ python3 manage.py makemigrations
$ python3 manage.py migrate
</div></code></pre>
<ul>
<li>session 示例</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-comment"># file : &lt;项目名&gt;/urls.py</span>
<span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span>  views

urlpatterns = [
    url(<span class="hljs-string">r'^admin/'</span>, admin.site.urls),
    <span class="hljs-comment"># 增删改session</span>
    url(<span class="hljs-string">r'^add_session'</span>, views.add_session),
    url(<span class="hljs-string">r'^mod_session/(\d+)'</span>, views.mod_session),
    url(<span class="hljs-string">r'^del_session'</span>, views.del_session),
    url(<span class="hljs-string">r'^show_session'</span>, views.show_session),
]
    
<span class="hljs-comment"># file : &lt;项目名&gt;/views.py</span>
<span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span> views
<span class="hljs-keyword">from</span> django.http <span class="hljs-keyword">import</span> HttpResponse
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">add_session</span><span class="hljs-params">(request)</span>:</span>
    request.session[<span class="hljs-string">'mysession_var'</span>] = <span class="hljs-number">100</span>
    responds = HttpResponse(<span class="hljs-string">"添加session"</span>)
    <span class="hljs-keyword">return</span> responds
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">mod_session</span><span class="hljs-params">(request, new_value)</span>:</span>
    request.session[<span class="hljs-string">'mysession_var'</span>] = new_value
    responds = HttpResponse(<span class="hljs-string">"修改session成功"</span>)
    <span class="hljs-keyword">return</span> responds
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">del_session</span><span class="hljs-params">(request)</span>:</span>
    <span class="hljs-keyword">try</span>:
        <span class="hljs-keyword">del</span> request.session[<span class="hljs-string">'mysession_var'</span>]
        responds = HttpResponse(<span class="hljs-string">"删除session成功"</span>)
    <span class="hljs-keyword">except</span>:
        responds = HttpResponse(<span class="hljs-string">"删除session失败"</span>)
    <span class="hljs-keyword">return</span> responds
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">show_session</span><span class="hljs-params">(request)</span>:</span>
    mysession_var = request.session.get(<span class="hljs-string">'mysession_var'</span>, <span class="hljs-string">'没有值!'</span>)
    print(<span class="hljs-string">"mysession_var = "</span>, mysession_var)
    <span class="hljs-keyword">return</span> HttpResponse(<span class="hljs-string">"mysession_var = "</span> + str(mysession_var))
</div></code></pre>
<h3 id="%E4%B8%AD%E9%97%B4%E4%BB%B6-middleware">中间件 Middleware</h3>
<ul>
<li>
<p>中间件是 Django 请求/响应处理的钩子框架。它是一个轻量级的、低级的“插件”系统，用于全局改变 Django 的输入或输出。</p>
</li>
<li>
<p>每个中间件组件负责做一些特定的功能。例如，Django 包含一个中间件组件 AuthenticationMiddleware，它使用会话将用户与请求关联起来。</p>
</li>
<li>
<p>他的文档解释了中间件是如何工作的，如何激活中间件，以及如何编写自己的中间件。Django 具有一些内置的中间件，你可以直接使用。它们被记录在 built-in middleware reference 中。</p>
</li>
<li>
<p>中间件类:</p>
<ul>
<li>中间件类须继承自 <code>django.utils.deprecation.MiddlewareMixin</code>类</li>
<li>中间件类须实现下列五个方法中的一个或多个:
<ul>
<li>
<p><code>def process_request(self, request):</code> 执行视图之前被调用，在每个请求上调用，返回None或HttpResponse对象</p>
</li>
<li>
<p><code>def process_view(self, request, callback, callback_args, callback_kwargs):</code> 调用视图之前被调用，在每个请求上调用，返回None或HttpResponse对象</p>
</li>
<li>
<p><code>def process_response(self, request, response):</code> 所有响应返回浏览器之前被调用，在每个请求上调用，返回HttpResponse对象</p>
</li>
<li>
<p><code>def process_exception(self, request, exception):</code> 当处理过程中抛出异常时调用，返回一个HttpResponse对象</p>
</li>
<li>
<p><code>def process_template_response(self, request, response):</code> 在视图刚好执行完毕之后被调用，在每个请求上调用，返回实现了render方法的响应对象</p>
</li>
</ul>
</li>
<li>注： 中间件中的大多数方法在返回None时表示忽略当前操作进入下一项事件，当返回HttpResponese对象时表示此请求结果，直接返回给客户端</li>
</ul>
</li>
<li>
<p>编写中间件类:</p>
</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-comment"># file : middleware/mymiddleware.py</span>
<span class="hljs-keyword">from</span> django.http <span class="hljs-keyword">import</span> HttpResponse, Http404
<span class="hljs-keyword">from</span> django.utils.deprecation <span class="hljs-keyword">import</span> MiddlewareMixin

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyMiddleWare</span><span class="hljs-params">(MiddlewareMixin)</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_request</span><span class="hljs-params">(self, request)</span>:</span>
        print(<span class="hljs-string">"中间件方法 process_request 被调用"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_view</span><span class="hljs-params">(self, request, callback, callback_args, callback_kwargs)</span>:</span>
        print(<span class="hljs-string">"中间件方法 process_view 被调用"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_response</span><span class="hljs-params">(self, request, response)</span>:</span>
        print(<span class="hljs-string">"中间件方法 process_response 被调用"</span>)
        <span class="hljs-keyword">return</span> response

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_exception</span><span class="hljs-params">(self, request, exception)</span>:</span>
        print(<span class="hljs-string">"中间件方法 process_exception 被调用"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_template_response</span><span class="hljs-params">(self, request, response)</span>:</span>
        print(<span class="hljs-string">"中间件方法 process_template_response 被调用"</span>)
        <span class="hljs-keyword">return</span> response
</div></code></pre>
<ul>
<li>注册中间件:</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-comment"># file : settings.py</span>
MIDDLEWARE = [
    ...
    <span class="hljs-string">'middleware.mymiddleware.MyMiddleWare'</span>,
]
</div></code></pre>
<ul>
<li>中间件的执行过程
<ul>
<li><img src="images/middleware.png" alt=""></li>
</ul>
</li>
</ul>
<!-- 
- 参考文档:
    - <https://blog.csdn.net/u010525694/article/details/81428213>
 -->
<ul>
<li>练习
<ul>
<li>用中间件实现强制某个IP地址只能向/test 发送一次GET请求</li>
<li>提示:
<ul>
<li>request.META['REMOTE_ADDR'] 可以得到远程客户端的IP地址</li>
<li>request.path_info 可以得到客户端访问的GET请求路由信息</li>
</ul>
</li>
<li>答案:<pre class="hljs"><code><div><span class="hljs-keyword">from</span> django.http <span class="hljs-keyword">import</span> HttpResponse, Http404
<span class="hljs-keyword">from</span> django.utils.deprecation <span class="hljs-keyword">import</span> MiddlewareMixin
<span class="hljs-keyword">import</span> re
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">VisitLimit</span><span class="hljs-params">(MiddlewareMixin)</span>:</span>
    <span class="hljs-string">'''此中间件限制一个IP地址对应的访问/user/login 的次数不能改过10次,超过后禁止使用'''</span>
    visit_times = {}  <span class="hljs-comment"># 此字典用于记录客户端IP地址有访问次数</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_request</span><span class="hljs-params">(self, request)</span>:</span>
        ip_address = request.META[<span class="hljs-string">'REMOTE_ADDR'</span>]  <span class="hljs-comment"># 得到IP地址</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> re.match(<span class="hljs-string">'^/test'</span>, request.path_info):
            <span class="hljs-keyword">return</span>
        times = self.visit_times.get(ip_address, <span class="hljs-number">0</span>)
        print(<span class="hljs-string">"IP:"</span>, ip_address, <span class="hljs-string">'已经访问过'</span>, times, <span class="hljs-string">'次!:'</span>, request.path_info)
        self.visit_times[ip_address] = times + <span class="hljs-number">1</span>
        <span class="hljs-keyword">if</span> times &lt; <span class="hljs-number">5</span>:
            <span class="hljs-keyword">return</span>

        <span class="hljs-keyword">return</span> HttpResponse(<span class="hljs-string">'你已经访问过'</span> + str(times) + <span class="hljs-string">'次，您被禁止了'</span>)
</div></code></pre>
</li>
</ul>
</li>
</ul>
<h4 id="%E8%B7%A8%E7%AB%99%E8%AF%B7%E6%B1%82%E4%BC%AA%E9%80%A0%E4%BF%9D%E6%8A%A4-csrf">跨站请求伪造保护 CSRF</h4>
<ul>
<li>跨站请求伪造攻击
<ul>
<li>某些恶意网站上包含链接、表单按钮或者JavaScript，它们会利用登录过的用户在浏览器中的认证信息试图在你的网站上完成某些操作，这就是跨站请求伪造。</li>
</ul>
</li>
<li>CSRF<pre class="hljs"><code><div>Cross-Site Request Forgey
跨     站点   请求    伪装
</div></code></pre>
</li>
<li>说明:
<ul>
<li>CSRF中间件和模板标签提供对跨站请求伪造简单易用的防护。</li>
</ul>
</li>
<li>作用:
<ul>
<li>不让其它表单提交到此 Django 服务器</li>
</ul>
</li>
<li>解决方案:
<ol>
<li>取消 csrf 验证(不推荐)
<ul>
<li>删除 settings.py 中 MIDDLEWARE 中的 <code>django.middleware.csrf.CsrfViewMiddleware</code> 的中间件</li>
</ul>
</li>
<li>开放验证<pre class="hljs"><code><div>在视图处理函数增加: @csrf_protect
@csrf_protect
def post_views(request):
    pass
</div></code></pre>
</li>
<li>通过验证<pre class="hljs"><code><div>需要在表单中增加一个标签 
{% csrf_token %}
</div></code></pre>
</li>
</ol>
<ul>
<li>练习: 项目的注册部分
<ol>
<li>创建一个数据库 - FruitDay</li>
<li>创建实体类 - Users
<ol>
<li>uphone - varchar(11)</li>
<li>upwd - varchar(50)</li>
<li>uemail - varchar(245)</li>
<li>uname - varchar(20)</li>
<li>isActive - tinyint 默认值为1 (True)</li>
</ol>
</li>
<li>完善注册 - /register/
<ol>
<li>如果是get请求,则去往register.html</li>
<li>如果是post请求,则处理请求数据
将提交的数据保存回数据库</li>
</ol>
</li>
</ol>
</li>
</ul>
</li>
</ul>
<h3 id="django%E4%B8%AD%E7%9A%84forms%E6%A8%A1%E5%9D%97">Django中的forms模块</h3>
<ul>
<li>在Django中提供了 forms 模块,用forms 模块可以自动生成form内部的表单控件,同时在服务器端可以用对象的形式接收并操作客户端表单元素，并能对表单的数据进行服务器端验证</li>
</ul>
<ol>
<li>
<p>forms模块的作用</p>
<ul>
<li>通过 forms 模块,允许将表单与class相结合，允许通过 class 生成表单</li>
</ul>
</li>
<li>
<p>使用 forms 模块的步骤</p>
<ol>
<li>
<p>在应用中创建 forms.py</p>
</li>
<li>
<p>导入 django 提供的 forms</p>
<ul>
<li>from django import forms</li>
</ul>
</li>
<li>
<p>创建class,一个class会生成一个表单</p>
<ul>
<li>定义表单类<pre class="hljs"><code><div>    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ClassName</span><span class="hljs-params">(forms.Form)</span>:</span>
        ...
</div></code></pre>
</li>
</ul>
</li>
<li>
<p>在 class 中创建类属性</p>
<ul>
<li>一个类属性对应到表单中是一个控件</li>
</ul>
</li>
<li>
<p>利用Form 类型的对象自动成表单内容</p>
</li>
<li>
<p>读取form表单并进行验证数据</p>
</li>
</ol>
</li>
<li>
<p>forms.Form 的语法</p>
<ul>
<li>属性 = forms.Field类型(参数)</li>
</ul>
<ol>
<li>类型<pre class="hljs"><code><div>class XXX(froms.Form):
    forms.CharField() : 文本框 &lt;input type=&quot;text&quot;&gt;
    forms.ChoiceField() : 下拉选项框 &lt;select&gt;
    forms.DateField() : 日期框 &lt;input type=&quot;date&quot;&gt;
    ... ...
</div></code></pre>
<ul>
<li>参见:&lt;&gt;</li>
</ul>
</li>
<li>参数
<ol>
<li>label
<ul>
<li>控件前的文本</li>
</ul>
</li>
<li>widget
<ul>
<li>指定小部件</li>
</ul>
</li>
<li>initial
<ul>
<li>控件的初始值(主要针对文本框类型)</li>
</ul>
</li>
<li>required
<ul>
<li>是否为必填项，值为(True/False)</li>
</ul>
</li>
</ol>
</li>
</ol>
</li>
</ol>
<ul>
<li>form 表单示例
<ul>
<li>
<p>手动实现Form 表单</p>
<pre class="hljs"><code><div><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"/test_form1"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"post"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"id_input_text"</span>&gt;</span>请输入内容:<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"input_text"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"id_input_text"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>提交<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</div></code></pre>
</li>
<li>
<p>Django Form 实现 Form 表单</p>
<pre class="hljs"><code><div><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MySearch</span><span class="hljs-params">(forms.Form)</span>:</span>
    input_text = forms.CharField(label = <span class="hljs-string">'请输入内容'</span>)
</div></code></pre>
</li>
</ul>
</li>
</ul>
<ol start="4">
<li>
<p>在模板中解析form对象</p>
<ol>
<li>
<p>方法</p>
<ol>
<li>需要自定义 <form></li>
<li>表单中的按钮需要自定义</li>
</ol>
</li>
<li>
<p>解析form对</p>
<pre class="hljs"><code><div>在 视图中创建form对象并发送到模板中解析.
ex:
    form = XXXForm()
    return render(request,'xx.html',locals())
</div></code></pre>
<ol>
<li>手动解析
{% for field in form %}
field : 表示的是form对象中的每个属性(控件)
{{field.label}} : 表示的是label参数值
{{field}} : 表示的就是控件
{% endfor %}</li>
</ol>
</li>
<li>
<p>自动解析</p>
<ol>
<li>{{form.as_p}}<br>
<code>将 form 中的每个属性(控件/文本)都使用p标记包裹起来再显示</code></li>
<li>{{form.as_ul}}<pre class="hljs"><code><div>将 form 中的每个属性(控件/文本)都使用li标记包裹起来再显示
注意:必须手动提供ol 或 ul 标记
</div></code></pre>
</li>
<li>{{form.as_table}}<pre class="hljs"><code><div>将 form 中的每个属性(控件/文本)都使用tr标记包裹起来再显示
注意:必须手动提供table标记
</div></code></pre>
</li>
</ol>
</li>
</ol>
<ul>
<li>练习:
<ol>
<li>创建一个注册Form类 - RegisterForm
<ul>
<li>username - 用户名称</li>
<li>password - 用户密码(文本框)</li>
<li>password2 - 重复用户密码(文本框)</li>
<li>phonenumber - 用户年龄(数字框)</li>
<li>email - 电子邮箱
2.创建 register 路由</li>
<li>get 请求 :
<ul>
<li>创建 RegisterForm 对象并发送到 模板register.html中显示</li>
</ul>
</li>
<li>post 请求:
<ul>
<li>接收13-register.html 中的数据并输出</li>
</ul>
</li>
</ul>
</li>
</ol>
</li>
</ul>
<ol start="5">
<li>通过 forms 对象获取表单数据
<ol>
<li>通过 forms.Form 子类的构造器来接收 post 数据
<ul>
<li>form = XXXForm(request.POST)</li>
</ul>
</li>
<li>必须是 form 通过验证后,才能取值
<ul>
<li>form.is_valid()
<ul>
<li>返回True:通过验证,可以取值</li>
<li>返回False:暂未通过验证,则不能取值</li>
</ul>
</li>
</ul>
</li>
<li>通过 form.cleaned_data 字典的属性接收数据
<ul>
<li>form.cleaned_data : dict 类型</li>
</ul>
</li>
</ol>
</li>
</ol>
</li>
</ol>
<!-- 
    6.forms 模块的高级处理
        允许将Models实体类和Forms模块结合到一起使用
        1.在 forms.py 中创建class
            继承自forms.ModelForm
        2.创建内部类Meta,关联Model类
            关联信息的属性如下:
            1.model : 指定要关联的实体类
            2.fields : 指定要从Model中取哪些字段生成控件
                1.取值为 "__all__"
                    取全部的属性生成控件
                2.取值为 列表
                    将允许生成控件的属性名放在列表中
            3.labels : 指定每个属性对应的label值
                取值为字典
                labels = {
                    '属性名':"label值"
                }
 -->
<ol start="7">
<li>
<p>Field 内置小部件 - widget</p>
<ol>
<li>什么是小部件
<ul>
<li>表示的是生成到网页上的控件以及一些其他的html属性</li>
</ul>
<pre class="hljs"><code><div>message=forms.CharField(widget=forms.Textarea)
upwd=forms.CharField(widget=forms.PasswordInput)
</div></code></pre>
</li>
<li>常用的小部件类型
<table>
<thead>
<tr>
<th>widget名称</th>
<th>对应和type类值</th>
</tr>
</thead>
<tbody>
<tr>
<td>TextInput</td>
<td>type='text'</td>
</tr>
<tr>
<td>PasswordInput</td>
<td>type='password'</td>
</tr>
<tr>
<td>NumberInput</td>
<td>type=&quot;number&quot;</td>
</tr>
<tr>
<td>EmailInput</td>
<td>type=&quot;email&quot;</td>
</tr>
<tr>
<td>URLInput</td>
<td>type=&quot;url&quot;</td>
</tr>
<tr>
<td>HiddenInput</td>
<td>type=&quot;hidden&quot;</td>
</tr>
<tr>
<td>CheckboxInput</td>
<td>type=&quot;checkbox&quot;</td>
</tr>
<tr>
<td>CheckboxSelectMultiple</td>
<td>type=&quot;checkbox&quot;</td>
</tr>
<tr>
<td>RadioSelect</td>
<td>type=&quot;radio&quot;</td>
</tr>
<tr>
<td>Textarea</td>
<td>textarea标记</td>
</tr>
<tr>
<td>Select</td>
<td>select标记</td>
</tr>
<tr>
<td>SelectMultiple</td>
<td>select multiple 标记</td>
</tr>
</tbody>
</table>
</li>
</ol>
</li>
<li>
<p>小部件的使用</p>
<ol>
<li>继承自forms.Form
<ol>
<li>基本版
<ol>
<li>语法<pre class="hljs"><code><div>属性 = forms.CharField() #无预选值使用
    text,password,email,url,textarea,checkbox
属性 = forms.ChoiceField() #有预选值使用
    checkbox,radio,select

属性 = forms.CharField(
    label='xxx',
    widget=forms.小部件类型
)
</div></code></pre>
</li>
<li>示例:<pre class="hljs"><code><div>upwd = forms.CharField(
    label='用户密码',
    widget=forms.PasswordInput
)

message = forms.CharField(
    label='评论内容',
    widget=forms.Textarea
)
</div></code></pre>
</li>
</ol>
</li>
<li>高级版
<ol>
<li>特征
<ul>
<li>在指定控件类型的基础之上还能指定控件的一些html属性值</li>
</ul>
</li>
<li>语法</li>
</ol>
<pre class="hljs"><code><div>    属性 = forms.CharField(
        label='xxx',
        widget=forms.小部件类型(
            attrs={
                'html属性名':'值',
                'html属性名':'值',
            }
        )
    )
</div></code></pre>
</li>
</ol>
</li>
</ol>
</li>
</ol>
<ul>
<li>文档参见<a href="https://yiyibooks.cn/xx/Django_1.11.6/topics/forms/index.html#forms-in-django">https://yiyibooks.cn/xx/Django_1.11.6/topics/forms/index.html#forms-in-django</a></li>
</ul>
<h4 id="django%E4%B9%8Bform%E8%A1%A8%E5%8D%95%E9%AA%8C%E8%AF%81">Django之form表单验证</h4>
<ul>
<li>
<p>django form 提供表单和字段验证</p>
</li>
<li>
<p>当在创建有不同的多个表单需要提交的网站时，用表单验证比较方便验证的封装</p>
</li>
<li>
<p>当调用form.is_valid() 返回True表示当前表单合法，当返回False说明表单验证出现问题</p>
</li>
<li>
<p>验证步骤:</p>
<ol>
<li>先对form.XXXField() 参数值进行验证，比如:min_length,max_length, validators=[...],如果不符合form.is_valid()返回False</li>
<li>对各自from.clean_zzz属性名(self): 方法对相应属性进行验证,如果验证失败form.is_valid()返回False</li>
<li>调胳form.clean(self): 对表单的整体结构进行验证，如果验证失败form.is_valid()返回False</li>
<li>以上验证都成功 form.is_valid()返回True</li>
</ol>
</li>
<li>
<p>验证方法:</p>
<ul>
<li>validators = [验证函数1, 验证函数1]
<ul>
<li>验证函数验证失败抛出forms.ValidationError</li>
<li>验证成功返回None</li>
</ul>
</li>
<li>def clean_xxx属性(self):
<ul>
<li>验证失败必须抛出forms.ValidationError</li>
<li>验证成功必须返回xxx属性的值</li>
</ul>
</li>
<li>def clean(self):
<ul>
<li>验证失败必须抛出forms.ValidationError</li>
<li>验证成功必须返回 self.cleaned_data</li>
</ul>
</li>
</ul>
</li>
<li>
<p>文档参见<a href="https://yiyibooks.cn/xx/Django_1.11.6/topics/forms/index.html#forms-in-django">https://yiyibooks.cn/xx/Django_1.11.6/topics/forms/index.html#forms-in-django</a></p>
</li>
<li>
<p>验证示例</p>
</li>
</ul>
<pre class="hljs"><code><div><span class="hljs-keyword">from</span> django <span class="hljs-keyword">import</span> forms
<span class="hljs-keyword">import</span> re

mobile_re = re.compile(<span class="hljs-string">r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$'</span>)
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">mobile_validate</span><span class="hljs-params">(value)</span>:</span>
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> mobile_re.match(value):
        <span class="hljs-keyword">raise</span> forms.ValidationError(<span class="hljs-string">'手机号码格式错误'</span>)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RegisterForm</span><span class="hljs-params">(forms.Form)</span>:</span>
    username = forms.CharField(label=<span class="hljs-string">'用户名'</span>)
    password = forms.CharField(label=<span class="hljs-string">'请输入密码'</span>, widget=forms.PasswordInput)
    password2 = forms.CharField(label=<span class="hljs-string">'再次输入新密码'</span>, widget=forms.PasswordInput)
    mobile = forms.CharField(label=<span class="hljs-string">'电话号码'</span>, validators=[mobile_validate])

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">clean</span><span class="hljs-params">(self)</span>:</span>
        pwd1 = self.cleaned_data[<span class="hljs-string">'password'</span>]
        pwd2 = self.cleaned_data[<span class="hljs-string">'password2'</span>]
        <span class="hljs-keyword">if</span> pwd1 != pwd2:
            <span class="hljs-keyword">raise</span> forms.ValidationError(<span class="hljs-string">'两次密码不一致!'</span>)
        <span class="hljs-keyword">return</span> self.cleaned_data  <span class="hljs-comment"># 必须返回cleaned_data</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">clean_username</span><span class="hljs-params">(self)</span>:</span>
        username = self.cleaned_data[<span class="hljs-string">'username'</span>]
        <span class="hljs-keyword">if</span> len(username) &lt; <span class="hljs-number">6</span>:
            <span class="hljs-keyword">raise</span> forms.ValidationError(<span class="hljs-string">"用户名太短"</span>)
        <span class="hljs-keyword">return</span> username
</div></code></pre>
<ul>
<li>练习，写一个RegisterForm表单类型,要求如下四个属性:
<ul>
<li>username - 用户名称
<ul>
<li>用户名只能包含[a-zA-Z_0_9]范围内的5~30个英文字符</li>
</ul>
</li>
<li>password - 用户密码(文本框)
<ul>
<li>任意字符，不能少于6个字符</li>
</ul>
</li>
<li>password2 - 重复用户密码(文本框)
<ul>
<li>任意字符，不能少于6个字符且必须与 password一致</li>
</ul>
</li>
<li>phonenumber - 用户年龄(数字框)
<ul>
<li>必须符合<code>r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$'</code>正规表达式</li>
</ul>
</li>
</ul>
</li>
</ul>

</body>
</html>
